﻿using System;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace CommonUtils
{
    /// <summary>
    /// 进程管理
    /// </summary>
    public class ProcessUtil
    {
        public static readonly Process Current = Process.GetCurrentProcess();

        /// <summary>
        /// 启动计算器
        /// </summary>
        public static void StartCalculator()
        => Process.Start("calc");

        public static ProcessExcuteInfo Run(ProcessStartInfo startInfo, double exitSeconds = 0, Encoding encoding = default)
        {
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = true;
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardError = true;
            startInfo.StandardOutputEncoding = Encoding.UTF8;
            startInfo.StandardErrorEncoding = Encoding.UTF8;
            if (encoding != default)
            {
                startInfo.StandardOutputEncoding = encoding;
                startInfo.StandardErrorEncoding = encoding;
            }

            var excuteInfo = new ProcessExcuteInfo();

            if (!string.IsNullOrEmpty(startInfo.WorkingDirectory))
                excuteInfo.StartInfo = "(WorkingDirectory)" + startInfo.WorkingDirectory + "\r\n";

            excuteInfo.StartInfo += startInfo.FileName.AddDoubleQuotes();

            if (!string.IsNullOrEmpty(startInfo.Arguments))
                excuteInfo.StartInfo += " " + startInfo.Arguments;

            if (exitSeconds > 0)
                excuteInfo.StartInfo += " 设定" + exitSeconds + "秒后超时退出";

            if (string.IsNullOrEmpty(startInfo.FileName))
            {
                excuteInfo.WarningOrError = "Process lost !";
                return excuteInfo;
            }

            try
            {
                excuteInfo.StartInfo.Print();

                var process = Process.Start(startInfo);

                //应该不对,弃用
                //while (!process.HasExited)
                //    process.Refresh();

                //卡程序,弃用 StandardError同理
                //process.StandardOutput.ReadLine();//这个可以关闭WaitForExit嵌到while循环里,但有时也会莫名卡死(弃用)
                //process.StandardOutput.ReadToEnd();//这个是拿最终结果,信息多时要等待很久(弃用)

                process.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e)
                {
                    excuteInfo.AppendOutputInfo(e.Data);
                };

                process.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs e)
                {
                    excuteInfo.AppendWarningOrError(e.Data);
                };

                process.BeginOutputReadLine();
                process.BeginErrorReadLine();

                if (exitSeconds > 0)
                {
                    process.WaitForExit((int)(exitSeconds * 1000));
                    if (!process.HasExited)
                        excuteInfo.AppendWarningOrError("程序超时,强制退出!");
                }
                else if (exitSeconds == 0)
                    process.WaitForExit();//程序可能会卡在这里
                else
                    ;//不等待程序,不检测状态,直接退出
                process.Close();
            }
            catch (Exception ex)
            {
                excuteInfo.AppendWarningOrError(ex.Message);
            }
            return excuteInfo;
        }

        /// <summary>
        /// 运行进程
        /// </summary>
        /// <param name="path">.net core环境下这里只能写exe .net环境可以直接写文件路径，框架会查找默认程序</param>
        /// <param name="args">参数</param>
        public static ProcessExcuteInfo Run(string path, string args = null, Encoding encoding = default)
        {
            var startInfo = new ProcessStartInfo(path);
            startInfo.Arguments = args;
            return Run(startInfo, 0, encoding);
        }

        //不等待结束
        public static ProcessExcuteInfo RunNwfe(string path, string args = null, Encoding encoding = default)
        {
            var startInfo = new ProcessStartInfo(path);
            startInfo.Arguments = args;
            return Run(startInfo, -1, encoding);
        }

        public static ProcessExcuteInfo RunAt(string workingDirectory, string path, string args = null)
        {
            var startInfo = new ProcessStartInfo(path);
            startInfo.WorkingDirectory = workingDirectory;
            startInfo.Arguments = args;
            return Run(startInfo);
        }

        public static ProcessExcuteInfo Start(ProcessStartInfo startInfo, double exitSeconds = 0)
        => Run(startInfo, exitSeconds);

        /// <summary>
        /// 运行进程
        /// </summary>
        /// <param name="path">.net core环境下这里只能写exe .net环境可以直接写文件路径，框架会查找默认程序</param>
        /// <param name="args">参数</param>
        public static ProcessExcuteInfo Excute(string path, string args = null)
        => Run(path, args);

        /// <summary>
        /// 运行进程
        /// </summary>
        /// <param name="path">.net core环境下这里只能写exe .net环境可以直接写文件路径，框架会查找默认程序</param>
        /// <param name="args">参数</param>
        public static ProcessExcuteInfo Open(string path, string args)
        => Excute(path, args);

        public static ProcessExcuteInfo Open(string path)
        => CommandUtil.Open(path);

        public static void OpenUrl(string url)
        //=> CommandUtil.Start(url);//无效
        //=> CommandUtil.Open(url);//含ReplaceSpritRight2Left,也能用
        => CommandUtil.OpenUrl(url);

        /// <summary>
        /// 关闭进程
        /// </summary>
        public static void Kill(int pid)
        {
            try
            {
                var process = Process.GetProcessById(pid);
                if (process != null)
                    process.Kill();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Pid: " + pid);
                Console.WriteLine(ex);
            }
        }

        /// <summary>
        /// 关闭进程
        /// </summary>
        public static void Kill(string name)
        {
            try
            {
                var processes = Process.GetProcessesByName(name);
                foreach (var process in processes)
                    process.Kill();
            }
            catch (Exception ex)
            {
                Console.WriteLine("ProcessName: " + name);
                Console.WriteLine(ex);
            }
        }
    }
}
